/*
 * Copyright (c) 2018-2024 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include "subcmd.h"

static void runjob_usage(const char *);

/**
 * @brief       qp job run handler, to force a job running in the cluster.
 *
 * @param[in]   argc          #1: number of input argument
 * @param[in]   argv          #2: string array of the input argument
 *
 * @retval      0             #1: succeed
 * @retval      1             #2: input option is wrong or kind of input argument not specified
 * @retval      2             #3: argument has syntax error
 * @retval      3             #4: failed to force the job running
 * @retval      4             #5: not enough memory
 */
int
cmd_run_job(int argc, char** argv)
{
    bool_t fOpt = FALSE;
    char *strNode = NULL;
    while (TRUE) {
        int option_index = 0;
        static struct option long_options[] = {
            {"help", no_argument, 0, 'h'},
            {"node", required_argument, 0, 'm'},
            {"force", no_argument, 0, 'f'},
            {NULL, 0, 0, 0}
        };

        int opt;
        opt = getopt_long(argc, argv, "hm:f", long_options, &option_index);
        if (-1 == opt) {
            break;
        }

        switch (opt) {
        case 'm':
            if (0 >= strlen(optarg)) {
                fprintf(stderr, "Specify a node name with -m/--node option.\n");
                runjob_usage(argv[0]);
                exit(1);
            }

            strNode = copy_string(optarg);
            break;
        case 'f':
            fOpt = TRUE;
            break;
        case 'h':
            runjob_usage(argv[0]);
            exit(0);
        case '?':
        default:
            runjob_usage(argv[0]);
            exit(0);
        }
    }

    if (argc <= optind) {
        fprintf(stderr, "Must specify a job ID.\n");
        runjob_usage(argv[0]);
        exit(1);
    }

    if (NULL == strNode) {
        fprintf(stderr, "Must specify -m/--node option when force run a job.\n");
        runjob_usage(argv[0]);
	exit(1);
    }

    // get the job ID for force running.
    JOBID_T jobId;
    if (get_one_jobid(argv[argc - 1], &jobId)) {
	exit(2);
    }

    unsigned int len = strlen(strNode);
    char *p = (char *)calloc(len+1, sizeof(char));
    if (NULL == p) {
        perror("calloc");
        exit(4);
    }

    char *head = strNode;
    unsigned int numNode = 0;
    char *token;
    while (NULL != (token = get_string_token(&head, " ", p, len+1))) {
        numNode++;
        len = strlen(head);
    }

    head = strNode;
    len = strlen(head);
    char **strNodeList = (char **)calloc(numNode, sizeof(char*));
    if (NULL == strNodeList) {
        perror("calloc");
        exit(4);
    }
    numNode = 0;
    while (NULL != (token = get_string_token(&head, " ", p, len+1))) {
        strNodeList[numNode++] = copy_string(token);
        len = strlen(head);
    }
    FREEUP(p);
    FREEUP(strNode);

    unsigned int options = (fOpt == TRUE) ? RUNJOB_OPT_NOSTOP : RUNJOB_OPT_NORMAL;

    struct request runjobRequest;
    memset(&runjobRequest, 0, sizeof(struct request));

    runjobRequest.number = 4;
    runjobRequest.keyvalues = (struct keyvalue *)calloc(4, sizeof(struct keyvalue));
    if (NULL == runjobRequest.keyvalues) {
        fprintf(stderr, "calloc(%ld) failed due to %m.\n",
                runjobRequest.number*sizeof(struct keyvalue));
        exit(4);
    }

    runjobRequest.keyvalues[0].key = REQUEST_KEY_OPTIONS;
    runjobRequest.keyvalues[0].value = (char *)calloc(12, sizeof(char));
    if (NULL == runjobRequest.keyvalues[0].value) {
        fprintf(stderr, "calloc(%d) failed due to %m.\n", 12);
        exit(4);
    }
    snprintf(runjobRequest.keyvalues[0].value, 12, "%d", options);

    runjobRequest.keyvalues[1].key = REQUEST_KEY_JOBIDS;
    runjobRequest.keyvalues[1].value = (char *)calloc(32, sizeof(char));
    if (NULL == runjobRequest.keyvalues[1].value) {
        fprintf(stderr, "calloc(%d) failed due to %m.\n", 32);
        exit(4);
    }
    snprintf(runjobRequest.keyvalues[1].value, 32, "%lld", jobId);

    runjobRequest.keyvalues[2].key = REQUEST_KEY_NUMBER;
    runjobRequest.keyvalues[2].value = (char *)calloc(12, sizeof(char));
    if (NULL == runjobRequest.keyvalues[2].value) {
        fprintf(stderr, "calloc(%d) failed due to %m.\n", 12);
        exit(4);
    }
    snprintf(runjobRequest.keyvalues[2].value, 12, "%d", numNode);

    unsigned int size, i;
    runjobRequest.keyvalues[3].key = REQUEST_KEY_NAMES;
    size = 0;
    for (i=0; i<numNode; i++) {
        size += strlen(strNodeList[i]) + 1;
    }
    runjobRequest.keyvalues[3].value = (char *)calloc(size+1, sizeof(char));
    if (NULL == runjobRequest.keyvalues[3].value) {
        fprintf(stderr, "calloc(%d) failed due to %m.\n", size+3);
        exit(4);
    }

    for (i=0; i<numNode; i++) {

        if (0 == i) {
            strcpy(runjobRequest.keyvalues[3].value, strNodeList[i]);
        } else {
            strcat(runjobRequest.keyvalues[3].value, " ");
            strcat(runjobRequest.keyvalues[3].value, strNodeList[i]);
        }
    }

    int ret;
    ret = client_runjob(&runjobRequest, NULL);
    if (0 > ret) {
        gf_xdr_free(gf_xdr_request, &runjobRequest);
        exit(3);
    }

    char strJobId[32];
    printf("Job <%s> is being forced to run.\n", jobid_2string(jobId, strJobId));
    gf_xdr_free(gf_xdr_request, &runjobRequest);

    exit(0);
} // end function cmd_run_job

static void
runjob_usage(const char *cmd)
{
    fprintf(stdout, "qp job %s usage: ", cmd);
    fprintf(stdout, "[-h, --help] [-f, --force] -m, --node host_name ... jobId | \"jobId[index]\"\n");

    return;
}
